"""
股票基础信息服务层

本文件实现股票基础信息的业务逻辑处理，包括CRUD操作、数据同步和导出等功能。
相关知识点：
- SQLAlchemy异步ORM (参考文档：技术知识点.md - 3. SQLAlchemy异步ORM)
- 异步编程 (参考文档：技术知识点.md - 5. 异步编程)
- AkShare财经数据接口 (参考文档：技术知识点.md - 4. AkShare财经数据接口)
"""
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List
import akshare as ak  # 财经数据接口库

# 导入异常和通用模型
from exceptions.exception import ServiceException  # 自定义业务异常
from module_admin.entity.vo.common_vo import CrudResponseModel  # CRUD响应模型

# 导入DAO和业务模型
from module_stock.dao.basic_dao import BasicDao  # 股票基础信息数据访问层
from module_stock.entity.vo.basic_vo import (
    DeleteBasicModel, BasicModel, BasicPageQueryModel
)  # 股票基础信息相关模型

# 导入工具类
from utils.ak_util import filter_ak_stock  # AkShare数据过滤工具
from utils.common_util import CamelCaseUtil  # 驼峰命名转换工具
from utils.excel_util import ExcelUtil  # Excel处理工具

class BasicService:
    """
    股票基础信息模块服务层
    
    提供股票基础信息的业务逻辑处理，包括：
    - 股票信息查询（分页、全部）
    - 股票信息增删改查
    - 从AkShare同步股票数据
    - 股票数据导出
    """

    @classmethod
    async def get_basic_list_services(
        cls, query_db: AsyncSession, query_object: BasicPageQueryModel, is_page: bool = False
    ):
        """
        获取股票基础信息列表信息
        
        相关知识点：
        - SQLAlchemy异步ORM (参考文档：技术知识点.md - 3. SQLAlchemy异步ORM)
        - 异步编程 (参考文档：技术知识点.md - 5. 异步编程)

        :param query_db: 数据库会话对象
        :param query_object: 查询参数对象
        :param is_page: 是否开启分页
        :return: 股票基础信息列表或分页结果
        """
        # 调用DAO层获取数据
        basic_list_result = await BasicDao.get_basic_list(query_db, query_object, is_page)
        return basic_list_result


    @classmethod
    async def add_basic_services(cls, query_db: AsyncSession, page_object: BasicModel):
        """
        新增股票基础信息
        
        相关知识点：
        - SQLAlchemy异步ORM (参考文档：技术知识点.md - 3. SQLAlchemy异步ORM)
        - 异步编程 (参考文档：技术知识点.md - 5. 异步编程)

        :param query_db: 数据库会话对象
        :param page_object: 新增股票基础信息对象
        :return: CRUD操作结果
        :raises Exception: 数据库操作异常
        """
        try:
            # 调用DAO层添加数据
            await BasicDao.add_basic_dao(query_db, page_object)
            # 提交事务
            await query_db.commit()
            return CrudResponseModel(is_success=True, message='新增成功')
        except Exception as e:
            # 发生异常时回滚事务
            await query_db.rollback()
            raise e

    @classmethod
    async def edit_basic_services(cls, query_db: AsyncSession, page_object: BasicModel):
        """
        编辑股票基础信息
        
        相关知识点：
        - SQLAlchemy异步ORM (参考文档：技术知识点.md - 3. SQLAlchemy异步ORM)
        - 异步编程 (参考文档：技术知识点.md - 5. 异步编程)

        :param query_db: 数据库会话对象
        :param page_object: 编辑股票基础信息对象
        :return: CRUD操作结果
        :raises ServiceException: 当股票信息不存在时抛出
        :raises Exception: 数据库操作异常
        """
        # 转换为字典，排除未设置的字段
        edit_basic = page_object.model_dump(exclude_unset=True, exclude={})
        
        # 先检查股票信息是否存在
        basic_info = await cls.basic_detail_services(query_db, page_object.symbol)
        if basic_info.symbol:
            try:
                # 调用DAO层更新数据
                await BasicDao.edit_basic_dao(query_db, edit_basic)
                # 提交事务
                await query_db.commit()
                return CrudResponseModel(is_success=True, message='更新成功')
            except Exception as e:
                # 发生异常时回滚事务
                await query_db.rollback()
                raise e
        else:
            # 股票信息不存在时抛出业务异常
            raise ServiceException(message='股票基础信息不存在')

    @classmethod
    async def delete_basic_services(cls, query_db: AsyncSession, page_object: DeleteBasicModel):
        """
        删除股票基础信息
        
        支持批量删除（逗号分隔的股票代码）
        
        相关知识点：
        - SQLAlchemy异步ORM (参考文档：技术知识点.md - 3. SQLAlchemy异步ORM)
        - 异步编程 (参考文档：技术知识点.md - 5. 异步编程)

        :param query_db: 数据库会话对象
        :param page_object: 删除参数对象，包含股票代码列表
        :return: CRUD操作结果
        :raises ServiceException: 当参数为空时抛出
        :raises Exception: 数据库操作异常
        """
        if page_object.symbols:
            # 分割股票代码字符串为列表
            symbol_list = page_object.symbols.split(',')
            try:
                # 遍历删除每个股票信息
                for symbol in symbol_list:
                    await BasicDao.delete_basic_dao(query_db, BasicModel(symbol=symbol))
                # 提交事务
                await query_db.commit()
                return CrudResponseModel(is_success=True, message='删除成功')
            except Exception as e:
                # 发生异常时回滚事务
                await query_db.rollback()
                raise e
        else:
            # 参数为空时抛出业务异常
            raise ServiceException(message='传入为空')

    @classmethod
    async def basic_detail_services(cls, query_db: AsyncSession, symbol: int):
        """
        获取股票基础信息详细信息
        
        相关知识点：
        - SQLAlchemy异步ORM (参考文档：技术知识点.md - 3. SQLAlchemy异步ORM)
        - 异步编程 (参考文档：技术知识点.md - 5. 异步编程)
        
        :param query_db: 数据库会话对象
        :param symbol: 股票代码
        :return: 股票基础信息对象

        :param query_db: orm对象
        :param symbol: 
        :return: 对应的信息
        """
        basic = await BasicDao.get_basic_detail_by_id(query_db, symbol=symbol)
        if basic:
            result = BasicModel(**CamelCaseUtil.transform_result(basic))
        else:
            result = BasicModel(**dict())

        return result

    @staticmethod
    async def export_basic_list_services(basic_list: List):
        """
        导出股票基础信息列表
        
        相关知识点：
        - Excel数据处理
        
        :param basic_list: 股票基础信息列表
        :return: Excel文件二进制数据

        :param basic_list: 股票基础信息信息列表
        :return: 股票基础信息信息对应excel的二进制数据
        """
        # 创建一个映射字典，将英文键映射到中文键
        mapping_dict = {
            'symbol': '',
            'name': '',
        }
        binary_data = ExcelUtil.export_list2excel(basic_list, mapping_dict)

        return binary_data
    
    @classmethod
    async def sync_ak_services(cls, query_db: AsyncSession):
        """
        从AkShare同步股票基础信息
        
        相关知识点：
        - AkShare财经数据接口 (参考文档：技术知识点.md - 4. AkShare财经数据接口)
        - SQLAlchemy异步ORM (参考文档：技术知识点.md - 3. SQLAlchemy异步ORM)
        - 异步编程 (参考文档：技术知识点.md - 5. 异步编程)
        
        :param query_db: 数据库会话对象
        :return: 同步结果消息
        1.从akshare获取股票列表数据
        2.查询股票基础信息表
        3
        3.1如果没有数据，说明是第一次同步，将akshare的数据直接插入数据库
        3.2如果有数据，则跟akshare的数据进行比对，进行增加、删除操作，说明akshare的增加了说明新股上市
        减少了说明是股票退市
        :param query_db: orm对象
        :return: 操作结果
        """
        try:
            datas_ak =  ak.stock_info_a_code_name()
            # 过滤主板股票
            datas_ak = filter_ak_stock(datas_ak, 'code', 'name')

            datas_ak = datas_ak[datas_ak['code'] ]
            symbol_list_ak =  datas_ak['code'].to_list()
            symbol_list_db = await BasicDao.get_all_symbols(query_db)

            if symbol_list_db and len(symbol_list_db) > 0 :
                #新增股票
                diff_symbols_ak = set(symbol_list_ak).difference(set(symbol_list_db))
                if diff_symbols_ak:
                    diff_datas_ak = datas_ak[datas_ak['code'].isin(diff_symbols_ak)]
                    diff_data_models = []
                    for diff_data in diff_datas_ak.itertuples():   
                        diff_data_models.append(BasicModel(symbol=diff_data.code, name=diff_data.name))
                    await BasicDao.batch_add_basic_dao(query_db, diff_data_models)
                #退市股票
                diff_symbols_db = set(symbol_list_db).difference(set(symbol_list_ak))
                if diff_symbols_db :
                    await BasicDao.batch_delete_basic_dao(query_db, diff_symbols_db)
            else:
                #没有数据，直接插入所有数据
                data_models = []
                for data_ak in datas_ak.itertuples():   
                    data_models.append(BasicModel(symbol=data_ak.code, name=data_ak.name))
                await BasicDao.batch_add_basic_dao(query_db, data_models)
            return CrudResponseModel(is_success=True, message='同步成功')
        except Exception as e:
            await query_db.rollback()
            raise e
